﻿#include "commandparser.h"
#include "ipfivetuple.h"

#include <QCommandLineParser>
#include <QCoreApplication>
#include <QHostAddress>

#include <data/startargs.h>

CommandParser::CommandParser()
{
    QCoreApplication::setApplicationName("QPerf");
    QCoreApplication::setApplicationVersion(QPERF_VERSION); //  使用宏，确保与Pro文件中的值一致。
    m_parser = new QCommandLineParser;
    m_parser->setApplicationDescription(QObject::tr("QPerf --- a tool for measure network status ."));
    m_parser->addHelpOption();
    m_parser->addVersionOption();

    initCmdOption();
}

CommandParser::~CommandParser()
{
    delete m_parser;
}

void CommandParser::process(QCoreApplication &app)
{
    m_parser->process(app);
}

bool CommandParser::bServer()
{
    return m_parser->isSet(m_options.at(Opt_Server));
}

bool CommandParser::bFake()
{
#ifdef QT_DEBUG
    return m_parser->isSet(m_options.at(Opt_Fake));
#else
    return false;
#endif
}

bool CommandParser::cmdIPTuple(IPFiveTuple &dest)
{
    bool server = bServer();
    quint16 cPort = 9988;
    cmdPort(cPort);

    QHostAddress cAddr;
    bool parsed = cmdAddress(cAddr);
    if(!server && !parsed)
    {
        return false;
    }
    IPFiveTuple::Type type = server ? IPFiveTuple::Type_TcpServer: IPFiveTuple::Type_TcpClient;
    dest.setType(type);
    if(server)
    {
        dest.setLocalAddr(cAddr);
        dest.setLocalPort(cPort);
    }else {
        dest.setRemoteAddr(cAddr);
        dest.setRemotePort(cPort);
    }
    return true;
}

bool CommandParser::dataIPTuple(IPFiveTuple &dest)
{
    IPFiveTuple::Type type = IPFiveTuple::Type_TcpServer;  // 默认使用tcpServer

    if(m_parser->isSet(m_options.at(Opt_UdpMode)))
    {
        type = IPFiveTuple::Type_Udp;
    }
    quint16 dPort = 8866;
    dataPort(dPort);

    QHostAddress dAddr;
    bool parsed = dataAddress(dAddr);
    if(!parsed)
    {
        return false;
    }
    dest.setType(type);
    dest.setRemoteAddr(dAddr);
    switch(type)
    {
    case IPFiveTuple::Type_Udp: // 设置远端。
    case IPFiveTuple::Type_TcpClient: // 设置远端。
        dest.setRemotePort(dPort);
        break;
    case IPFiveTuple::Type_TcpServer: // 设置本地端。
        dest.setLocalPort(dPort);
        break;
    }
    return true;
}

bool CommandParser::startArgs(StartArgs &dest)
{
    IPFiveTuple tuple;
    if(dataIPTuple(tuple) == true)
    {
        int timeOut = 1000;
        fetchInt(m_options.at(Opt_Timeout), timeOut);

        int sampling = 10000;
        fetchInt(m_options.at(Opt_Frequency), sampling);

        int packSize = 1000;
        fetchInt(m_options.at(Opt_PacketSize), packSize);

        int total = 100000;
        fetchInt(m_options.at(Opt_Total), total);

        int warmPack = 10;
        fetchInt(m_options.at(Opt_Warm), warmPack);

        dest.setTimeOut(1000);
        dest.setPackFreq(sampling);
        dest.setPerPackSize(packSize);
        dest.setTotalCount(total);
        dest.setIpTuple(tuple);
        dest.setWarmPack(warmPack);
        return true;
    }
    return false;
}

void CommandParser::showHelp()
{
    m_parser->showHelp();
}

bool CommandParser::cmdPort(quint16 &port)
{
    return fetchPort(m_options.at(Opt_CmdPort), port);
}

bool CommandParser::cmdAddress(QHostAddress &host)
{
    return fetchAddress(m_options.at(Opt_CmdAddress), host);
}

bool CommandParser::dataPort(quint16 &port)
{
    return fetchPort(m_options.at(Opt_DataPort), port);
}

bool CommandParser::dataAddress(QHostAddress &host)
{
    return fetchAddress(m_options.at(Opt_CmdAddress), host); // 数据IP与命令IP应该一致。
}

bool CommandParser::fetchInt(const QCommandLineOption &name, int &dest)
{
    QString data = m_parser->value(name);
    bool ok;
    int value = data.toInt(&ok);
    if(ok)
    {
        dest = value;
        return true;
    }
    return false;
}
bool CommandParser::fetchPort(const QCommandLineOption &name, quint16 &port)
{
    QString data = m_parser->value(name);
    bool ok;
    int cPort = data.toInt(&ok);
    if(ok && cPort < 65536 && cPort > 0)
    {
        port = quint16(cPort);
        return true;
    }
    return false;
}

bool CommandParser::fetchAddress(const QCommandLineOption &name, QHostAddress &host)
{
    QString data = m_parser->value(name);
    if(data.isEmpty())
    {
        return  false;
    }
    QHostAddress address(data);
    if(address.isNull())
    {
        return false;
    }
    host.swap(address);
    return true;
}

void CommandParser::initCmdOption()
{
    int typeCount = CommandParser::Opt_TypeCount;
    // 初始命令参数，加入m_options 的顺序与OptionType定义的顺序需一致。
    QCommandLineOption cmdPortOpt(QStringList() << "p" << "cport",
                                     QObject::tr("Command link port, <port>."), "port");
    m_options.append(cmdPortOpt);

    QCommandLineOption cmdAddrOpt(QStringList() << "a" << "caddr",
                                     QObject::tr("Command link address, <address>. "), "address");
    m_options.append(cmdAddrOpt);

    QCommandLineOption dataPortOpt(QStringList() << "P" << "dport",
                                      QObject::tr("Data link port."), "port");
    m_options.append(dataPortOpt);

    QCommandLineOption serverOpt(QStringList() << "s" << "server",
                                    QObject::tr("Run with server mode,default in client mode."));
    m_options.append(serverOpt);

    QCommandLineOption udpOpt(QStringList() << "u" << "udp", QObject::tr("Data link use udp."));
    m_options.append(udpOpt);

    QCommandLineOption timeoutOpt(QStringList() << "t" << "timout",
                                  QObject::tr("Timeout (ms),default 1000ms."), "timeout");
    m_options.append(timeoutOpt);

    QCommandLineOption freqOpt(QStringList() << "r" << "frequency",
                               QObject::tr("Send packet frequency per second, default 10000."), "freq");
    m_options.append(freqOpt);

    QCommandLineOption totalOpt(QStringList() << "o" << "total",
                                  QObject::tr("Total packet send, default 100000."), "total packet");
    m_options.append(totalOpt);

    QCommandLineOption perSizeOpt(QStringList() << "S" << "packSize",
                                  QObject::tr("Send perpacket size(in bytes), default 1000."), "packSize");
    m_options.append(perSizeOpt);

    QCommandLineOption warmOpt(QStringList() << "w" << "warm",
                                  QObject::tr("Test warm packet, default 10."), "warm packet size");
    m_options.append(warmOpt);

#ifdef QT_DEBUG
    QCommandLineOption fakeOpt(QStringList() << "f" << "fake", QObject::tr("Run progress in fake ."));
    m_options.append(fakeOpt);
#endif
    Q_ASSERT(typeCount == m_options.count()); // 必须相等。
    m_parser->addOptions(m_options);
}

